//---------------------------------------------------------------------------
//    parameter_handler.cc,v 1.81 2005/09/08 23:58:24 wolf Exp
//    Version: Version-5-2-0
//
//    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//---------------------------------------------------------------------------

#include "ParameterHandler.h"
#include "LogStream.h"
#include "Utilities.h"

#include <fstream>
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <algorithm>
#include <list>
#include <sstream>
#include <limits>

//TODO[WB]: various functions here could be simplified by using namespace Utilities

namespace Patterns
{
  PatternBase::~PatternBase ()
  {}



  const int Integer::min_int_value = std::numeric_limits<int>::min();

  const int Integer::max_int_value = std::numeric_limits<int>::max();

  Integer::Integer (const int lower_bound,
                    const int upper_bound)
                  :
      lower_bound (lower_bound),
      upper_bound (upper_bound)
  {}

  bool Integer::match (const std::string &test_string) const
  {
    std::istringstream str(test_string);

    int i;
    if (str >> i)
      {
          // check whether valid bounds
          // were specified, and if so
          // enforce their values
        if (lower_bound <= upper_bound)
          return ((lower_bound <= i) && (upper_bound >= i));
        else
          return true;
      };

    return false;
  }

  std::string Integer::description () const
  {
            // check whether valid bounds
            // were specified, and if so
            // output their values
    if (lower_bound <= upper_bound)
      {
        std::ostringstream description;

        description << "[Integer range "
                    << lower_bound << "..." << upper_bound
                    << " (inclusive)]";
        return description.str();
      }
    else
              // if no bounds were given, then
              // return generic string
      return "[Integer]";
  }

  PatternBase *
  Integer::clone () const
  {
    return new Integer(lower_bound, upper_bound);
  }

  const double Double::min_double_value = -std::numeric_limits<double>::max();

  const double Double::max_double_value = std::numeric_limits<double>::max();

  Double::Double (const double lower_bound,
                  const double upper_bound)
                  :
      lower_bound (lower_bound),
      upper_bound (upper_bound)
  {}

  bool Double::match (const std::string &test_string) const
  {
    std::istringstream str(test_string);

    double d;
    if (str >> d)
      {
          // check whether valid bounds
          // were specified, and if so
          // enforce their values
        if (lower_bound <= upper_bound)
          return ((lower_bound <= d) && (upper_bound >= d));
        else
          return true;
      }
    return false;
  }

  std::string Double::description () const
  {
            // check whether valid bounds
            // were specified, and if so
            // output their values
    if (lower_bound <= upper_bound)
      {
        std::ostringstream description;

        description << "[Floating point range "
                    << lower_bound << "..." << upper_bound
                    << " (inclusive)]";
        return description.str();
      }
    else
              // if no bounds were given, then
              // return generic string
      return "[Double]";
  }

  PatternBase *
  Double::clone () const
  {
    return new Double(lower_bound, upper_bound);
  }

  Selection::Selection (const std::string &seq)
  {
    sequence = seq;

    while (sequence.find(" |") != std::string::npos)
      sequence.replace (sequence.find(" |"), 2, "|");
    while (sequence.find("| ") != std::string::npos)
      sequence.replace (sequence.find("| "), 2, "|");
  }

  bool Selection::match (const std::string &test_string) const
  {
    std::vector<std::string> choices;
    std::string tmp(sequence);
            // check the different possibilities
    while (tmp.find('|') != std::string::npos) 
      {
        if (test_string == std::string(tmp, 0, tmp.find('|')))
          return true;

        tmp.erase (0, tmp.find('|')+1);
      };
            // check last choice, not finished by |
    if (test_string == tmp)
      return true;

            // not found
    return false;
  }

  std::string Selection::description () const
  {
    return sequence;
  }

  PatternBase *
  Selection::clone () const
  {
    return new Selection(sequence);
  }

  const unsigned int List::max_int_value = std::numeric_limits<unsigned int>::max();

  List::List (const PatternBase  &p,
              const unsigned int  min_elements,
              const unsigned int  max_elements)
                  :
                  pattern (p.clone()),
                  min_elements (min_elements),
                  max_elements (max_elements)
  {
    Assert (min_elements <= max_elements,
            ExcInvalidRange (min_elements, max_elements));
  }

  List::~List ()
  {
    delete pattern;
    pattern = 0;
  }

  bool List::match (const std::string &test_string_list) const
  {
    std::string tmp = test_string_list;
    std::vector<std::string> split_list;
    split_list.reserve (std::count (tmp.begin(), tmp.end(), ',')+1);

            // first split the input list
    while (tmp.length() != 0)
      {
        std::string name;
        name = tmp;

        if (name.find(",") != std::string::npos)
          {
            name.erase (name.find(","), std::string::npos);
            tmp.erase (0, tmp.find(",")+1);
          }
        else
          tmp = "";

        while ((name.length() != 0) && (name[0] == ' '))
          name.erase (0,1);

        while (name[name.length()-1] == ' ')
          name.erase (name.length()-1, 1);

        split_list.push_back (name);
      };

    if ((split_list.size() < min_elements) || (split_list.size() > max_elements))
      return false;

            // check the different possibilities
    for (std::vector<std::string>::const_iterator
         test_string = split_list.begin();
         test_string != split_list.end(); ++test_string)
      if (pattern->match (*test_string) == false)
        return false;

    return true;
  }

  std::string List::description () const
  {
    std::ostringstream description;	

    description << "list of <" << pattern->description() << ">" 
                << " of length " << min_elements << "..." << max_elements
                << " (inclusive)";

    return description.str();
  }

  PatternBase *
  List::clone () const
  {
    return new List(*pattern, min_elements, max_elements);
  }


  MultipleSelection::MultipleSelection (const std::string &seq)
  {
    Assert (seq.find (",") == std::string::npos, ExcCommasNotAllowed(seq.find(",")));

    sequence = seq;
    while (sequence.find(" |") != std::string::npos)
      sequence.replace (sequence.find(" |"), 2, "|");
    while (sequence.find("| ") != std::string::npos)
      sequence.replace (sequence.find("| "), 2, "|");
  }

  bool MultipleSelection::match (const std::string &test_string_list) const
  {
    std::string tmp = test_string_list;
    std::list<std::string> split_list;

            // first split the input list
    while (tmp.length() != 0)
      {
        std::string name;
        name = tmp;

        if (name.find(",") != std::string::npos)
          {
            name.erase (name.find(","), std::string::npos);
            tmp.erase (0, tmp.find(",")+1);
          }
        else
          tmp = "";

        while ((name.length() != 0) &&
              (name[0] == ' '))
          name.erase (0,1);
        while (name[name.length()-1] == ' ')
          name.erase (name.length()-1, 1);

        split_list.push_back (name);
      };


            // check the different possibilities
    for (std::list<std::string>::const_iterator test_string = split_list.begin();
         test_string != split_list.end(); ++test_string)
      {
        bool string_found = false;

        tmp = sequence;
        while (tmp.find('|') != std::string::npos)
          {
            if (*test_string == std::string(tmp, 0, tmp.find('|')))
              {
                  // string found, quit
                  // loop. don't change
                  // tmp, since we don't
                  // need it anymore.
                string_found = true;
                break;
              };

            tmp.erase (0, tmp.find('|')+1);
          };
                  // check last choice, not finished by |
        if (!string_found)
          if (*test_string == tmp)
            string_found = true;

        if (!string_found)
          return false;
      };

    return true;
  }

  std::string MultipleSelection::description () const
  {
    return sequence;
  }

  PatternBase *
  MultipleSelection::clone () const
  {
    return new MultipleSelection(sequence);
  }

  Bool::Bool ()
                  :
      Selection ("true|false")
  {}

  PatternBase *
  Bool::clone () const
  {
    return new Bool();
  }

  Anything::Anything ()
  {}

  bool Anything::match (const std::string &) const
  {
    return true;
  }

  std::string Anything::description () const
  {
    return "[Anything]";
  }

  PatternBase *
  Anything::clone () const
  {
    return new Anything();
  }

}   // end namespace Patterns

ParameterHandler::ParameterHandler ()
{}

ParameterHandler::~ParameterHandler ()
{}

bool ParameterHandler::read_input (std::istream &input)
{
  AssertThrow (input, ExcIO());

  std::string line;
  int lineno=0;
  bool status = true;

  while (input) 
    {
      ++lineno;
      getline (input, line);
      if (!scan_line (line, lineno)) 
        status = false;
    }

  return status;
}

bool ParameterHandler::read_input (const std::string &filename)
{
  std::ifstream input (filename.c_str());
  if (!input) 
    {
      std::cerr << "ParameterHandler::read_input: could not open file <"
                << filename << "> for reading." << std::endl
                << "Trying to make file <"
                << filename << "> with default values for you." << std::endl;

      std::ofstream output (filename.c_str());
      if (output)
        print_parameters (output, Text);

      return false;
    }

  return read_input (input);
}

bool ParameterHandler::read_input_from_string (const char *s)
{
          // if empty std::string then exit
          // with success
  if ((s == 0) || ((*s) == 0)) return true;

  std::string line;
  std::string input (s);
  int    lineno=0;

          // if necessary append a newline char
          // to make all lines equal
  if (input[input.length()-1] != '\n')
    input += '\n';

  bool status = true;
  while (input.size() != 0) 
    {
              // get one line from Input (=s)
      line.assign (input, 0, input.find('\n'));
              // delete this part including
              // the backspace
      input.erase (0, input.find('\n')+1);
      ++lineno;

      if (!scan_line (line, lineno)) 
        status = false;
    }

  return status;
}

void ParameterHandler::clear ()
{
  subsection_path.clear ();
  defaults.entries.clear ();
  changed_entries.entries.clear ();

  std::map<std::string, Section*>::iterator p;

  for (p=defaults.subsections.begin(); p!=defaults.subsections.end(); ++p)
    delete p->second;

  for (p=changed_entries.subsections.begin(); p!=changed_entries.subsections.end(); ++p)
    if (p->second)
      {
        delete p->second;
        Assert (false, ExcInternalError());
      }

  defaults.subsections.clear ();
  changed_entries.subsections.clear ();
}

void
ParameterHandler::declare_entry (const std::string           &entry,
                                 const std::string           &default_value,
                                 const Patterns::PatternBase &pattern,
                                 const std::string           &documentation)
{
  Section* p = get_present_defaults_subsection ();

          // default must match Pattern
  Assert (pattern.match (default_value),
    ExcDefaultDoesNotMatchPattern(default_value,
          pattern.description()));

          // if the entry already existed,
          // make sure we don't create a
          // memory leak
  if (p->entries.find (entry) != p->entries.end())
    delete p->entries[entry].pattern;

                                  // if the entry didn't yet exist,
                                  // but map::operator[] will create
                                  // it
  p->entries[entry].value         = default_value;
  p->entries[entry].documentation = documentation;
  p->entries[entry].pattern       = pattern.clone();
}

void ParameterHandler::enter_subsection (const std::string &subsection)
{
  Section* pd = get_present_defaults_subsection ();

          // does subsection already exist?
  if (pd->subsections.find (subsection) == pd->subsections.end()) 
    {
              // subsection does not yet exist:
              // create entry in Defaults and
              // changed_entries trees
      pd->subsections[subsection] = new Section();

      Section* pc = get_present_changed_subsection ();
      pc->subsections[subsection] = new Section();
    };

          // finally enter subsection
  subsection_path.push_back (subsection);
}

bool ParameterHandler::leave_subsection ()
{
          // assert there is a subsection that
          // we may leave
          // (use assert since this is a logical
          // error in a program. When reading input
          // the scan_line function has to check
          // whether there is a subsection to be left!)
  Assert (subsection_path.size() != 0, ExcAlreadyAtTopLevel());

  if (subsection_path.size() == 0) 
    return false;

  subsection_path.pop_back ();
  return true;
}

const std::string & ParameterHandler::get (const std::string &entry_string) const
{
  const Section* pd = get_present_defaults_subsection ();
  const Section* pc = get_present_changed_subsection ();

          // assert that the according entry is already
          // declared in the defaults tree
  Assert (pd->entries.find (entry_string) != pd->entries.end(),
    ExcEntryUndeclared(entry_string));

  if (pd->entries.find (entry_string) == pd->entries.end()) 
    {
      static const std::string empty_string;
      return empty_string;
    };


          // entry exists; now find out
          // whether it was changed:
  Section::EntryType::const_iterator ptr;
  ptr = pc->entries.find (entry_string);
  if (ptr != pc->entries.end())
    return ptr->second.value;

          // not changed. take the value from
          // the defaults tree
  return pd->entries.find(entry_string)->second.value;
}

long int ParameterHandler::get_integer (const std::string &entry_string) const
{
  std::string s = get (entry_string);
  char *endptr;
  long int i = std::strtol (s.c_str(), &endptr, 10);
          // assert there was no error
  AssertThrow ((s.c_str()!='\0') || (*endptr == '\0'),
        ExcConversionError(s));

  return i;
}

double ParameterHandler::get_double (const std::string &entry_string) const
{
  std::string s = get (entry_string);
  char *endptr;
  double d = std::strtod (s.c_str(), &endptr);
          // assert there was no error
  AssertThrow ((s.c_str()!='\0') || (*endptr == '\0'),
        ExcConversionError(s));

  return d;
}


bool ParameterHandler::get_bool (const std::string &entry_string) const
{
  std::string s = get(entry_string);

  AssertThrow ((s=="true") || (s=="false") || (s=="yes") || (s=="no"), ExcConversionError(s));
  if (s=="true" || s=="yes")
    return true;
  else
    return false;
}



std::ostream &
ParameterHandler::print_parameters (std::ostream     &out,
                                    const OutputStyle style)
{
          // assert that only known formats are
          // given as "style"
  Assert ((style == Text) || (style == LaTeX), ExcNotImplemented());

  AssertThrow (out, ExcIO());

  switch (style)
    {
      case Text:
        out << "# Listing of Parameters" << std::endl
            << "# ---------------------" << std::endl;
        break;
      case LaTeX:
        out << "\\subsubsection*{Listing of parameters}";
        out << std::endl << std::endl;
        out << "\\begin{itemize}"
            << std::endl;
        break;
      default:
        Assert (false, ExcNotImplemented());
    };

          // dive recursively into the subsections
  print_parameters_section (out, style, 0);

  switch (style) 
    {
      case Text:
        break;
      case LaTeX:
        out << "\\end{itemize}" << std::endl;
        break;
      default:
        Assert (false, ExcNotImplemented());
    };

  return out;
}


void
ParameterHandler::print_parameters_section (std::ostream      &out,
                                            const OutputStyle  style,
                                            const unsigned int indent_level)
{
          // assert that only known formats are
          // given as "style"
  Assert ((style == Text) || (style == LaTeX), ExcNotImplemented());

  AssertThrow (out, ExcIO());

  Section *pd = get_present_defaults_subsection ();
  Section *pc = get_present_changed_subsection ();

          // traverse entry list
  Section::EntryType::const_iterator ptr;

  switch (style) 
    {
      case Text:
      {
                                        // first find out the longest
                                        // entry name to be able to
                                        // align the equal signs
        unsigned int longest_name = 0;
        for (ptr = pd->entries.begin(); ptr != pd->entries.end(); ++ptr)
          if (ptr->first.length() > longest_name)
            longest_name = ptr->first.length();

                                        // likewise find the longest
                                        // actual value string to
                                        // make sure we can align the
                                        // default and documentation
                                        // strings
        unsigned int longest_value = 0;
        for (ptr = pd->entries.begin(); ptr != pd->entries.end(); ++ptr) 
          longest_value
            = std::max (static_cast<size_t> (longest_value),
                        (pc->entries.find(ptr->first) != pc->entries.end()
                        ?
                        pc->entries[ptr->first].value
                        :
                        pd->entries[ptr->first].value).length());

                                        // print entries one by one
        for (ptr = pd->entries.begin(); ptr != pd->entries.end(); ++ptr) 
          {
                                            // see whether the
                                            // value is listed in
                                            // the Changed tree and
                                            // if so take it from
                                            // there. otherwise
                                            // take the default
                                            // value from the
                                            // Default tree
            const std::string &value
              = (pc->entries.find(ptr->first) != pc->entries.end()
                ?
                pc->entries[ptr->first].value
                :
                pd->entries[ptr->first].value);

                                            // if there is documentation,
                                            // then add an empty line (unless
                                            // this is the first entry in a
                                            // subsection), print the
                                            // documentation, and then the
                                            // actual entry; break the
                                            // documentation into readable
                                            // chunks such that the whole
                                            // thing is at most 78 characters
                                            // wide
            if (pd->entries[ptr->first].has_documentation())
              {
                if (ptr != pd->entries.begin())
                  out << std::endl;

                const std::vector<std::string> doc_lines
                  = Utilities::
                  breakTextIntoLines (pd->entries[ptr->first].documentation,
                                        78 - indent_level*2 - 2);

                for (unsigned int i=0; i<doc_lines.size(); ++i)
                  out << std::setw(indent_level*2) << ""
                      << "# "
                      << doc_lines[i]
                      << std::endl;
              }



                                            // print name and value
                                            // of this entry
            out << std::setw(indent_level*2) << ""
                << "set "
                << ptr->first
                << std::setw(longest_name-ptr->first.length()+1) << " "
                << "= " << value;

                                            // finally print the
                                            // default value, but
                                            // only if it differs
                                            // from the actual value
            if (value != pd->entries[ptr->first].value)
              {
                out << std::setw(longest_value-value.length()+1) << " "
                    << "# ";
                out << "default: " << pd->entries[ptr->first].value;
              }

            out << std::endl;
          }

        break;
      }

      case LaTeX:
      {
                                        // print entries one by one
        for (ptr = pd->entries.begin(); ptr != pd->entries.end(); ++ptr)
          {
                                            // print name and value
            out << "\\item {\\bf " << ptr->first << ":} "
                << pd->entries[ptr->first].value
                << " (";

                                            // if there is a
                                            // documenting string,
                                            // print it as well
            if (pd->entries[ptr->first].documentation.length() != 0)
              out << pd->entries[ptr->first].documentation << ", ";

                                            // finally print default
                                            // value
            out << "{\\it default:} "
                << (pc->entries.find(ptr->first) != pc->entries.end()
                    ?
                    pc->entries[ptr->first].value
                    :
                    pd->entries[ptr->first].value)
                << ")"
                << std::endl;
          }
        break;
      }

      default:
            Assert (false, ExcNotImplemented());
    }


                                  // if there was text before and there are
                                  // sections to come, put two newlines
                                  // between the last entry and the first
                                  // subsection; also make sure that the
                                  // subsections will be printed at all
                                  // (i.e. at least one of them is non-empty)
  if ((pd->entries.size() != 0)
      &&
      (pd->subsections.size() != 0)
      &&
      (pd->accumulated_no_of_entries() != pd->entries.size()))
    out << std::endl << std::endl;

          // now transverse subsections tree; only
          // plot a subsection, if it has at least
          // one entry somewhere in it
  std::map<std::string, Section*>::const_iterator ptrss;
  for (ptrss = pd->subsections.begin();
      ptrss != pd->subsections.end(); ++ptrss)
    if (ptrss->second->accumulated_no_of_entries() != 0)
      {
                                        // first print the subsection header
        switch (style) 
          {
            case Text:
                  out << std::setw(indent_level*2) << ""
                      << "subsection " << ptrss->first << std::endl;
                  break;
            case LaTeX:
                  out << std::endl
                      << "\\item {\\bf "
                      << "Subsection " << ptrss->first
                      << "}" << std::endl
                      << "\\begin{itemize}"
                      << std::endl;
                  break;
            default:
                  Assert (false, ExcNotImplemented());
          };

                                        // then the contents of the
                                        // subsection
        enter_subsection (ptrss->first);
        print_parameters_section (out, style, indent_level+1);
        leave_subsection ();
        switch (style) 
          {
            case Text:
                                                  // write end of
                                                  // subsection. one
                                                  // blank line after
                                                  // each subsection
                  out << std::setw(indent_level*2) << ""
                      << "end" << std::endl
                      << std::endl;

                                                  // if this is a toplevel
                                                  // subsection, then have two
                                                  // newlines
                  if (indent_level == 0)
                    out << std::endl;

                  break;
            case LaTeX:
                  out << "\\end{itemize}"
                      << std::endl;
                  break;
            default:
                  Assert (false, ExcNotImplemented());
          }
      }
}



void
ParameterHandler::log_parameters (LogStream &out)
{
  out.push("parameters");
          // dive recursively into the
          // subsections
  log_parameters_section (out);

  out.pop();
}



void
ParameterHandler::log_parameters_section (LogStream &out)
{
  Section *pd = get_present_defaults_subsection ();
  Section *pc = get_present_changed_subsection ();

          // traverse entry list
  Section::EntryType::const_iterator ptr;

          // print entries one by one
  for (ptr = pd->entries.begin(); ptr != pd->entries.end(); ++ptr)
                                    // see whether the value is
                                    // listed in the Changed tree
                                    // and if so take it from
                                    // there. otherwise take the
                                    // default value from the
                                    // Default tree
    if (pc->entries.find(ptr->first) != pc->entries.end())
      out << ptr->first << ": "
          << pc->entries[ptr->first].value << std::endl;
    else
      out << ptr->first << ": "
          << ptr->second.value << std::endl;

          // now transverse subsections tree
  std::map<std::string, Section*>::const_iterator ptrss;
  for (ptrss = pd->subsections.begin(); ptrss != pd->subsections.end(); ++ptrss)
    {
      out.push(ptrss->first);
      enter_subsection (ptrss->first);
      log_parameters_section (out);
      leave_subsection ();
      out.pop();
    };
}



bool
ParameterHandler::scan_line (std::string        line,
                            const unsigned int lineno)
{
          // if there is a comment, delete it
  if (line.find('#') != std::string::npos)
    line.erase (line.find("#"), std::string::npos);
          // replace every whitespace sequence
          // by " "
  while (line.find('\t') != std::string::npos)
    line.replace (line.find('\t'), 1, " ");
  while (line.find("  ") != std::string::npos)
    line.erase (line.find("  "), 1);
            // now every existing whitespace
          // should be exactly one ' ';
          // if at end or beginning: delete
  if ((line.length() != 0) && (line[0] == ' '))  line.erase (0, 1);
          // if line is now empty: leave
  if (line.length() == 0) return true;

  if (line[line.length()-1] == ' ')
    line.erase (line.size()-1, 1);

          // enter subsection
  if ((line.find ("SUBSECTION ") == 0) ||
      (line.find ("subsection ") == 0))
    {
              // delete this prefix
      line.erase (0, std::string("subsection").length()+1);

      std::string SecName = line;
      Section* pc = get_present_changed_subsection ();
              // check whether subsection exists
      if (pc->subsections.find(SecName) == pc->subsections.end()) 
        {
          std::cerr << "Line " << lineno
                          << ": There is no such subsection to be entered:" << std::endl;
          for (unsigned int i=0; i<subsection_path.size(); ++i)
            std::cerr << std::setw(i*2+4) << " "
                << "subsection " << subsection_path[i] << std::endl;
          std::cerr << std::setw(subsection_path.size()*2+4) << " "
              << "subsection " << SecName << std::endl;
          return false;
        };

              // subsection exists
      subsection_path.push_back (SecName);
      return true;
    };

          // exit subsection
  if ((line.find ("END") == 0) ||
      (line.find ("end") == 0))
    if (subsection_path.size() == 0) 
      {
        std::cerr << "Line " << lineno
                  << ": There is no subsection to leave here!" << std::endl;
        return false;
      }
    else
      return leave_subsection ();

          // regular entry
  if ((line.find ("SET ") == 0) ||
      (line.find ("set ") == 0)) 
    {
              // erase "set" statement and eliminate
              // spaces around the '='
      line.erase (0, 4);
      if (line.find(" =") != std::string::npos)
        line.replace (line.find(" ="), 2, "=");
      if (line.find("= ") != std::string::npos)
        line.replace (line.find("= "), 2, "=");

              // extract entry name and value
      std::string entry_name  (line, 0, line.find('='));
      std::string entry_value (line, line.find('=')+1, std::string::npos);

      Section* pd = get_present_defaults_subsection ();

              // check whether entry was declared
      if (pd->entries.find(entry_name) == pd->entries.end()) 
        {
          std::cerr << "Line " << lineno
              << ": No such entry was declared:" << std::endl
              << "    " << entry_name << std::endl
              << "    <Present subsection:" << std::endl;
          for (unsigned int i=0; i<subsection_path.size(); ++i)
            std::cerr << std::setw(i*2+8) << " "
                << "subsection " << subsection_path[i] << std::endl;
          std::cerr << "    >" << std::endl;

          return false;
        };

              // if entry was declared:
              // does it match the regex? if not,
              // don't enter it into the database
              // exception: if it contains characters
              // which specify it as a multiple loop
              // entry, then ignore content
      if (entry_value.find ('{') == std::string::npos)
        if (!pd->entries[entry_name].pattern->match(entry_value))
          {
            std::cerr << "Line " << lineno << ":" << std::endl
                << "    The entry value" << std::endl
                << "        " << entry_value << std::endl
                << "    for the entry named" << std::endl
                << "        " << entry_name << std::endl
                << "    does not match the given pattern" << std::endl
                << "        " << pd->entries[entry_name].pattern->description()
                            << std::endl;
            return false;
          };

      Section* pc = get_present_changed_subsection ();
              // the following lines declare
              // this entry if not yet
              // existent and overwrites it
              // otherwise (the pattern is
              // set to a null pointer, since
              // we don't need the pattern in
              // the entries section -- only
              // in the defaults section)
      pc->entries[entry_name].value   = entry_value;
      pc->entries[entry_name].pattern = 0;

      return true;
    };

          // this line matched nothing known
  std::cerr << "Line " << lineno
            << ": This line matched nothing known:" << std::endl
            << "    " << line << std::endl;
  return false;
}


ParameterHandler::Section* ParameterHandler::get_present_defaults_subsection ()
{
  Section* sec = &defaults;
  std::vector<std::string>::const_iterator SecName = subsection_path.begin();

  while (SecName != subsection_path.end()) 
    {
      sec = sec->subsections[*SecName];
      ++SecName;
    };

  return sec;
}



const ParameterHandler::Section* ParameterHandler::get_present_defaults_subsection () const
{
  Section* sec = const_cast<Section*>(&defaults); // not nice, but needs to be and
          // after all: we do not change @p{sec}
  std::vector<std::string>::const_iterator SecName = subsection_path.begin();

  while (SecName != subsection_path.end()) 
    {
      sec = sec->subsections[*SecName];
      ++SecName;
    };

  return sec;
}



ParameterHandler::Section* ParameterHandler::get_present_changed_subsection ()
{
  Section* sec = &changed_entries;
  std::vector<std::string>::iterator SecName = subsection_path.begin();

  while (SecName != subsection_path.end()) 
    {
      sec = sec->subsections[*SecName];
      ++SecName;
    };

  return sec;
}



const ParameterHandler::Section* ParameterHandler::get_present_changed_subsection () const
{
  Section* sec = const_cast<Section*>(&changed_entries); // same as in get_present_default_s...
  std::vector<std::string>::const_iterator SecName = subsection_path.begin();

  while (SecName != subsection_path.end()) 
    {
      sec = sec->subsections[*SecName];
      ++SecName;
    }

  return sec;
}


ParameterHandler::Section::~Section ()
{
          // first release the memory pointed
          // to by the second component of
          // the pair, since we became owner
          // of that memory through the
          // clone() call
  for (EntryType::iterator q=entries.begin(); q!=entries.end(); ++q)
    delete q->second.pattern;
          // then clear entire map
  entries.clear ();

  std::map<std::string, Section*>::iterator p;

  for (p=subsections.begin(); p!=subsections.end(); ++p)
    delete p->second;

  subsections.clear ();
}


unsigned int
ParameterHandler::Section::
accumulated_no_of_entries () const
{
                                  // number of entries in this section
  unsigned int n = entries.size();

                                  //  then accumulate over all children
  for (std::map<std::string, Section*>::const_iterator
       s = subsections.begin();
       s != subsections.end(); ++s)
    n += s->second->accumulated_no_of_entries();

  return n;
}


bool
ParameterHandler::Section::EntryContent::
has_documentation () const
{
  return documentation != "";
}


MultipleParameterLoop::UserClass::~UserClass ()
{}


MultipleParameterLoop::MultipleParameterLoop()
                :
    n_branches(0)
{}


MultipleParameterLoop::~MultipleParameterLoop ()
{}


bool MultipleParameterLoop::read_input (std::istream &input)
{
  AssertThrow (input, ExcIO());

  bool x = ParameterHandler::read_input (input);
  if (x)
    init_branches ();
  return x;
}



bool MultipleParameterLoop::read_input (const std::string &filename)
{
  return ParameterHandler::read_input (filename);
          // don't call init_branches, since this read_input
          // function calls
          // MultipleParameterLoop::Readinput(std::istream &, std::ostream &)
          // which itself calls init_branches.
}



bool MultipleParameterLoop::read_input_from_string (const char *s)
{
  bool x = ParameterHandler::read_input (s);
  init_branches ();
  return x;
}



void MultipleParameterLoop::loop (MultipleParameterLoop::UserClass &uc)
{
  for (unsigned int run_no=0; run_no<n_branches; ++run_no) 
    {
              // give create_new one-based numbers
      uc.create_new (run_no+1);
      fill_entry_values (run_no);
      uc.run (*this);
    };
}



void MultipleParameterLoop::init_branches ()
{
  multiple_choices.clear ();

  ParameterHandler::Section *sec;
          // first check the defaults entries whether it
          // contains multiple choices
  sec = &defaults;
  init_branches_section (*sec);

          // then check changed entries
  sec = &changed_entries;
  init_branches_section (*sec);

          // split up different values
  for (unsigned int i=0; i<multiple_choices.size(); ++i)
    multiple_choices[i].split_different_values ();
  
          // check whether we have included a multiple
          // choice entry from the defaults section which
          // has only one single value after reading the
          // input
  if (multiple_choices.size() > 0)
    for (std::vector<Entry>::iterator i=multiple_choices.end()-1;
         i >= multiple_choices.begin(); --i)
      if (i->different_values.size() == 1)
        multiple_choices.erase (i);

          // finally calculate number of branches
  n_branches = 1;
  for (unsigned int i=0; i<multiple_choices.size(); ++i)
    if (multiple_choices[i].type == Entry::variant)
      n_branches *= multiple_choices[i].different_values.size();

          // check whether array entries have the correct
          // number of entries
  for (unsigned int i=0; i<multiple_choices.size(); ++i)
    if (multiple_choices[i].type == Entry::array)
      if (multiple_choices[i].different_values.size() != n_branches)
        std::cerr << "    The entry value" << std::endl
                  << "        " << multiple_choices[i].entry_value << std::endl
                  << "    for the entry named" << std::endl
                  << "        " << multiple_choices[i].entry_name << std::endl
                  << "    does not have the right number of entries for the " << std::endl
                  << "        " << n_branches << " variant runs that will be performed."
                  << std::endl;


          // do a first run on filling the values to
          // check for the conformance with the regexp
          // (afterwards, this will be lost in the whole
          // other output)
  for (unsigned int i=0; i<n_branches; ++i) 
    fill_entry_values (i);
}


void MultipleParameterLoop::init_branches_section (const ParameterHandler::Section &sec)
{
          // check all entries in the present subsection
          // whether it is a multiple entry
  Section::EntryType::const_iterator e;
  for (e = sec.entries.begin(); e != sec.entries.end(); ++e) 
    if (e->second.value.find('{') != std::string::npos) 
      multiple_choices.push_back (Entry(subsection_path,
          e->first,
          e->second.value));


          // transverse subsections
  std::map<std::string, Section*>::const_iterator s;
  for (s = sec.subsections.begin(); s != sec.subsections.end(); ++s) 
    {
      enter_subsection (s->first);
      init_branches_section (*s->second);
      leave_subsection ();
    };
}



void MultipleParameterLoop::fill_entry_values (const unsigned int run_no)
{
  int possibilities = 1;

  std::vector<Entry>::iterator choice;
  for (choice = multiple_choices.begin();
      choice != multiple_choices.end();
      ++choice)
    {
              // temporarily enter the subsection tree
              // of this multiple entry
      subsection_path.swap (choice->subsection_path);

              // set entry
      Section* pd = get_present_defaults_subsection ();
      int selection = (run_no/possibilities) % choice->different_values.size();
      std::string entry_value;
      if (choice->type == Entry::variant)
        entry_value = choice->different_values[selection];
      else 
        {
          if (run_no>=choice->different_values.size())
            {
              std::cerr << "The given array for entry <"
            << choice->entry_name
            << "> does not contain enough elements! Taking empty string instead."
                              << std::endl;
              entry_value = "";
            }
          else
            entry_value = choice->different_values[run_no];
        }

              // check conformance with regex
      if (!pd->entries[choice->entry_name].pattern->match(entry_value))
        {
          std::cerr << "In run no.  " << run_no+1 << ":" << std::endl
              << "    The entry value" << std::endl
              << "        " << entry_value << std::endl
              << "    for the entry named" << std::endl
              << "        " << choice->entry_name << std::endl
              << "    does not match the given pattern" << std::endl
              << "        " << pd->entries[choice->entry_name].pattern->description() << std::endl
              << "    Taking default value" << std::endl
              << "        " << pd->entries[choice->entry_name].value << std::endl;

                  // select default instead
          entry_value = pd->entries[choice->entry_name].value;
        }

      Section* pc = get_present_changed_subsection ();
              // the following lines declare
              // this entry if not yet
              // existent and overwrites it
              // otherwise (the pattern is
              // set to a null pointer, since
              // we don't need the pattern in
              // the entries section -- only
              // in the defaults section)
      pc->entries[choice->entry_name].value   = entry_value;
      pc->entries[choice->entry_name].pattern = 0;

              // get out of subsection again
      subsection_path.swap (choice->subsection_path);

              // move ahead if it was a variant entry
      if (choice->type == Entry::variant)
        possibilities *= choice->different_values.size();
    };
}


MultipleParameterLoop::Entry::Entry (const std::vector<std::string> &ssp,
                                     const std::string              &Name,
                                     const std::string              &Value)
                :
    subsection_path (ssp), entry_name(Name), entry_value(Value)
{}


void MultipleParameterLoop::Entry::split_different_values ()
{
          // split string into three parts:
          // part before the opening "{",
          // the selection itself, final
          // part after "}"
  std::string prefix  (entry_value, 0, entry_value.find('{'));
  std::string multiple(entry_value, entry_value.find('{')+1,
          entry_value.rfind('}')-entry_value.find('{')-1);
  std::string postfix (entry_value, entry_value.rfind('}')+1, std::string::npos);
          // if array entry {{..}}: delete inner
          // pair of braces
  if (multiple[0]=='{')
    multiple.erase (0,1);
  if (multiple[multiple.size()-1] == '}')
    multiple.erase (multiple.size()-1, 1);
          // erase leading and trailing spaces
          // in multiple
  while (multiple[0] == ' ') multiple.erase (0,1);
  while (multiple[multiple.size()-1] == ' ') multiple.erase (multiple.size()-1,1);

          // delete spaces around '|'
  while (multiple.find(" |") != std::string::npos)
    multiple.replace (multiple.find(" |"), 2, "|");
  while (multiple.find("| ") != std::string::npos)
    multiple.replace (multiple.find("| "), 2, "|");

  while (multiple.find('|') != std::string::npos) 
    {
      different_values.push_back (prefix +
          std::string(multiple, 0, multiple.find('|'))+
          postfix);
      multiple.erase (0, multiple.find('|')+1);
    };
          // make up the last selection ("while" broke
          // because there was no '|' any more
  different_values.push_back (prefix+multiple+postfix);
          // finally check whether this was a variant
          // entry ({...}) or an array ({{...}})
  if ((entry_value.find("{{") != std::string::npos) &&
      (entry_value.find("}}") != std::string::npos))
    type = Entry::array;
  else
    type = Entry::variant;
}
